<?php

namespace App\B2BProject\Custom\Business;

use App\B2BProject\Area;
use App\B2BProject\AttachmentPrivateFiles;
use App\B2BProject\Custom\Purchaser\ShoppingCart;
use App\B2BProject\Exception\UserTypeException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\Eloquent\Model;
use stdClass;
use Symfony\Component\HttpFoundation\File\UploadedFile;

/**
 * Class Order
 *
 * 订单模型
 *
 * 订单号长度为固定的 24 位，
 * 前 8 位为时间：20151102；随后 10 位为用户编号，如编号为 12 的用户此号码为：0000000012；最后 6 位为序号：000188；
 *
 * 订单状态：1 订单创建成功，等待物流报价；2 待付款；3 已付款，待发货；4 已发货；5 已收货；6 资金结算完毕，订单流程结束；
 *
 * @package App\B2BProject\Custom\Business
 */
class Order extends Model
{
    /**
     * 通过购物车创建订单
     *
     * 该方法返回一个订单对象数组，因为购物车中的商品可能属于不同商家，对于不同商家订单会独立创建。
     *
     * @param Address      $address
     * @param Invoice|null $invoice
     *
     * @throws UserTypeException 用户类型错误异常
     * @throws ModelNotFoundException 模型未找到异常
     * @throws \BadMethodCallException 错误的方法调用异常
     *
     * @return array
     */
    public static function createOrderFromShoppingCart(Address $address, Invoice $invoice = null, $message = '')
    {
        // 判断用户类型
        if (\Auth::user()->isPurchaser()) {
            $items = \Auth::user()->shoppingCart()->with('goods')->get();

            $stores = [];

            // 按照商品的不同商家划分成多个订单，订单只能针对一个商家
            foreach ($items as $item) {
                $stores[$item->goods->store->id][] = $item;
            }

            $result = [];

            foreach ($stores as $storeGoods) {
                $result[] = static::createOrderByManyShoppingCartGoods($storeGoods, $address, $invoice, $message);
            }

            return $result;
        }

        throw new UserTypeException;
    }

    /**
     * 将多个购物车对象用于创建订单
     *
     * @param array        $items
     * @param Address      $address
     * @param Invoice|null $invoice
     * @param string       $message
     *
     * @throws ModelNotFoundException
     * @throws \BadMethodCallException
     *
     * @return mixed
     */
    public static function createOrderByManyShoppingCartGoods(
        array $items,
        Address $address,
        Invoice $invoice = null,
        $message = ''
    ) {
        return \DB::transaction(function () use ($items, $address, $invoice, $message) {
            /**
             * @var Store $store
             */
            $store = $items[0]->goods->store;

            // 获取当前用户最后一次订单的序列编号，我们会根据此订单序号 + 1 的方式生成下一个订单序号以保证不冲突
            $lastOrder     = \Auth::user()->orders()->orderBy('created_at', 'desc')->orderBy('id', 'desc')->first();
            $lastOrderCode = is_null($lastOrder) ? 0 : $lastOrder->order_code;
            unset($lastOrder);
            $orderCode = static::orderCodeGenerate((int)substr($lastOrderCode, -8) + 1);

            // 创建订单主体
            $order = new static;

            $order->order_code = $orderCode;

            // 收货人基本信息
            $order->delivery_address      = sprintf("%s %s %s %s", Area::getName($address->province_id),
                Area::getName($address->city_id), Area::getName($address->county_id), $address->detail_area);
            $order->consignee             = $address->name;
            $order->consignee_information = json_encode([
                'code'     => $address->code,
                'telphone' => $address->tel_phone,
                'phone'    => $address->mob_phone,
            ]);
            $order->logistics_price       = 0;
            $order->logistics_number      = '';
            $order->logistics_receipts    = '';
            $order->logistics_company     = '';

            // 发票基本信息
            if (is_null($invoice)) {
                $order->invoice_type    = 1;
                $order->invoice_subject = '';
                $order->invoice_content = json_encode(new stdClass());
            } else {
                $order->invoice_type    = $invoice->type;
                $order->invoice_subject = $invoice->subject;
                $VAT                    = \Auth::user()->VAT;
                if ($VAT) {
                    $order->invoice_content = json_encode([
                        'company_name'     => $VAT->company_name,
                        'code'             => $VAT->code,
                        'address'          => $VAT->address,
                        'phone'            => $VAT->phone,
                        'bank'             => $VAT->bank,
                        'bank_account'     => $VAT->bank_account,
                        'bill_to_name'     => $VAT->bill_to_name,
                        'bill_to_address'  => $VAT->bill_to_address,
                        'bill_to_phone'    => $VAT->bill_to_phone,
                        'bill_to_province' => $VAT->bill_to_province,
                    ]);
                }
            }

            // 设置为 0 表示还未选择支付方式
            $order->pay_type = 0;

            // 订单创建时的留言
            $order->message = $message ?: '';

            // 完成订单创建
            $order->save();

            // 初始化商品价格总额
            $totalPrice = 0;

            foreach ($items as $item) {
                $specification = new OrderSpecification();

                /**
                 * @var Goods $goods
                 */
                $goods = Goods::findOrFail($item->goods_id);

                if (!$goods->sell_status) {
                    throw new \BadMethodCallException('当前商品不可购买（可能被删除或禁售）');
                }

                // 对商品数量进行相应的扣除
                $goods->stockCompute(-(int)$item->amount, $item->goods_spec_id ?: null);

                /**
                 * @var GoodsSpecification $goodsSpec
                 */
                $goodsSpec
                    = $goods->hasSpecification() ? $goods->goodsSpecification()->whereId($item->goods_spec_id)->firstOrFail() : null;

                if (($strategy = $goods->getPriceStrategy()) === false) {
                    throw new \BadMethodCallException('该商品属于未授权商品，严禁操作');
                }

                // 获取商品相关信息，个别数据进行缓存以防止商品数据变动
                // TODO 后期改为 通过关联模型设置这类 ID
                $specification->goods_id               = $item->goods_id;
                $specification->goods_name             = $goods->name;
                $specification->amount                 = $item->amount;
                $specification->original_price
                                                       = $goodsSpec ? $goodsSpec->original_price : $goods->original_price;
                $specification->price_strategy         = $strategy;
                $specification->goods_cover            = $goods->cover();
                $specification->goods_spec_information = json_encode(call_user_func_array(function (
                    Goods $goods,
                    GoodsSpecification $specification = null
                ) {
                    if (is_null($specification)) {
                        return new stdClass();
                    }

                    return [
                        [$goods->spec_first_name, $specification->spec_first],
                        [$goods->spec_second_name, $specification->spec_second],
                        [$goods->spec_third_name, $specification->spec_third],
                        [$goods->spec_forth_name, $specification->spec_forth],
                        [$goods->spec_fifth_name, $specification->spec_fifth],
                    ];
                }, [$goods, $goodsSpec]));

                $specification->save();

                // 计算入订单商品价格总额
                $totalPrice += $specification->original_price * $specification->price_strategy * $item->amount;

                // 将商品写入订单表
                $order->specifications()->save($specification);

                // 从购物车清理相应的对象
                $item->delete();
            }

            $order->total_amount = $totalPrice;

            // 通过关联模型保存
            $order->store()->associate($store);
            $order->provider()->associate($store->member);
            $order->purchaser()->associate(\Auth::user());

            $order->save();

            return $order;
        });
    }

    /**
     * 生成订单号
     *
     * @param int $sn 序列号
     *
     * @return string
     */
    public static function orderCodeGenerate($sn)
    {
        return sprintf("%8d%08d%08d", date('Ymd', time()), \Auth::id(), $sn);
    }

    /**
     * 通过 ShoppingCart 对象快速创建订单，常用于立即购买功能
     *
     * @param ShoppingCart $cart
     * @param Address      $address
     * @param Invoice|null $invoice
     * @param string       $message
     *
     * @throws ModelNotFoundException
     * @throws \BadMethodCallException
     *
     * @return array
     */
    public static function createOrder(ShoppingCart $cart, Address $address, Invoice $invoice = null, $message = '')
    {
        return [static::createOrderByManyShoppingCartGoods([$cart], $address, $invoice, $message)];
    }

    /**
     * 订单的具体物品列表
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function specifications()
    {
        return $this->hasMany('App\B2BProject\Custom\Business\OrderSpecification', 'order_id', 'id');
    }

    /**
     * 获取物流单据文件对象
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function logisticsReceipts()
    {
        return $this->belongsTo('App\B2BProject\AttachmentPrivateFiles', 'logistics_receipts', 'id');
    }

    /**
     * 物流报价和商品价格调整
     *
     * 前提：订单状态在 1 （待物流报价）
     *
     * @param float      $logisticsPrice 物流价格
     * @param float|null $goodsPrice     商品总价，为 NULL 表示不调整
     *
     * @throws UserTypeException
     * @throws \BadMethodCallException 调用该方法时，未满足前提则会抛出该异常
     *
     * @return $this
     */
    public function offer($logisticsPrice, $goodsPrice = null)
    {
        if (\Auth::user()->isProvider()) {

            // 判断状态是否正常
            if ($this->status != 1 && $this->status != 2) {
                $this->operateHistory()->save(OrderOperatorHistory::operating('adjust-status', \Auth::user(), '状态调整失败',
                    '状态异常', false));
                throw new \BadMethodCallException('订单操作异常！当前订单状态不允许进行该操作！');
            }

            return \DB::transaction(function () use ($logisticsPrice, $goodsPrice) {
                $this->logistics_price = $logisticsPrice;

                $original_price = $this->total_amount;

                // 不为空表示需要调整商品总价
                if (!is_null($goodsPrice)) {
                    $this->total_amount = $goodsPrice;
                }

                // 状态进入待付款阶段，该阶段依旧可以调整价格
                if ($this->status == 1) {
                    $this->operateHistory()->save(OrderOperatorHistory::operating('adjust-price-logistics', \Auth::user(),
                        json_encode([
                            'logistics'   => ['from' => 0, 'to' => $logisticsPrice],
                            'order_price' => ['from' => $original_price, 'to' => $goodsPrice],
                        ])));
                    $this->status = 2;
                } else {
                    $this->operateHistory()->save(OrderOperatorHistory::operating('adjust-price-only', \Auth::user(),
                        json_encode([
                            'logistics'   => ['from' => 0, 'to' => $logisticsPrice],
                            'order_price' => ['from' => $original_price, 'to' => $goodsPrice],
                        ])));
                }

                $this->save();

                return $this;
            });
        }

        throw new UserTypeException;
    }

    /**
     * 支付当前订单
     *
     * 前提：订单状态在 2（待付款）
     *
     * @throws UserTypeException
     * @throws \BadMethodCallException 调用该方法时，未满足前提则会抛出该异常
     */
    public function pay()
    {
        if (\Auth::user()->isPurchaser()) {
            // 判断状态是否正常
            if ($this->status != 2) {
                throw new \BadMethodCallException('订单操作异常！当前订单状态不允许进行该操作！');
            }

            // TODO 【注意】该功能保留，待支付接口提供后再进行后续开发
        }

        throw new UserTypeException;
    }

    /**
     * 对当前订单调整为发货状态
     *
     * 前提，订单状态在 3（已付款）
     *
     * @param string       $logistics         物流方式或物流公司名
     * @param string       $logisticsNumber   物流单号
     * @param UploadedFile $logisticsReceipts 物流单据图片、文件
     *
     * @throws UserTypeException
     * @throws \BadMethodCallException 调用该方法时，未满足前提则会抛出该异常
     *
     * @return $this
     */
    public function deliver($logistics, $logisticsNumber, UploadedFile $logisticsReceipts)
    {
        if (\Auth::user()->isProvider()) {

            // 判断状态是否正常
            if ($this->status != 3) {
                throw new \BadMethodCallException('订单操作异常！当前订单状态不允许进行该操作！');
            }

            return \DB::transaction(function () use ($logistics, $logisticsNumber, $logisticsReceipts) {
                $this->logistics_company = $logistics;
                $this->logistics_number  = $logisticsNumber;

                // 处理物流单据
                $file = AttachmentPrivateFiles::setNewUploadFile($logisticsReceipts,
                    sprintf("/orders/%s", $this->order_code));

                $this->logisticsReceipts()->associate($this);

                // 状态进入待收货阶段
                $this->status = 3;

                $this->save();

                return $this;
            });
        }

        throw new UserTypeException;
    }

    /**
     * 订单关联的店铺
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function store()
    {
        return $this->belongsTo('App\B2BProject\Custom\Business\Store', 'store_id', 'id');
    }

    /**
     * 订单关联的卖家用户
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function provider()
    {
        return $this->belongsTo('App\B2BProject\Custom\Member', 'provider_id', 'id');
    }

    /**
     * 订单关联的买家用户
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function purchaser()
    {
        return $this->belongsTo('App\B2BProject\Custom\Member', 'member_id', 'id');
    }

    /**
     * 获取当前订单总价
     *
     * @return float
     */
    public function getTotalAmount()
    {
        return $this->total_amount + $this->logistics_price;
    }

    /**
     * 获取当前订单操作历史
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function operateHistory()
    {
        return $this->hasMany('App\B2BProject\Custom\Business\OrderOperatorHistory', 'order_id', 'id');
    }

    /**
     * 用户信息对应的用户
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function member()
    {
        return $this->belongsTo('App\B2BProject\Custom\Member');
    }
}
